Date: Thu Feb 27 22:59:42 2020
Scientist: Ran Yin
Sequencing (Waksman): Dibyendu Kumar (?)
Statistics: Davit Sargsyan
Principal Investigator: Ah-Ng Kong

Sources

Data

FastQ files were downloaded from this Rutgers Box location. A total of 144 files (2 per sample, pair-ended) and a pair of undetermined reads were downloaded.

16s metadata Sep-2019.xlsx meta-data file was created by Ran. It was saved as .CSV file and used by this script.

Load libraries

Meta-data

# dt.meta <- fread("data_jan2020/16s metadata Sep-2019.csv")
# save(dt.meta,
#      file = "data_sep2019/dt.meta.RData")
# datatable(dt.meta,
#           caption = "Table 1: Meta-Data",
#           rownames = FALSE,
#           class = "cell-border stripe",
#           options = list(searching = TRUE,
#                          pageLength = 8))

NOTE: moved “Undetermined” samples from the FastQ folder to the docs folder.

Questions

  1. Did microbiome change over time?
  2. Was microbiome affected by diet?
  3. Was microbiome affected by KO compared to WT?

FastQ files

# Get FastQ file names----
list.files(path = path,
           pattern = ".gz")
 [1] "RH01_S1_L001_R1_001.fastq.gz"  "RH01_S1_L001_R2_001.fastq.gz"  "RH02_S2_L001_R1_001.fastq.gz" 
 [4] "RH02_S2_L001_R2_001.fastq.gz"  "RH03_S3_L001_R1_001.fastq.gz"  "RH03_S3_L001_R2_001.fastq.gz" 
 [7] "RH04_S4_L001_R1_001.fastq.gz"  "RH04_S4_L001_R2_001.fastq.gz"  "RH05_S5_L001_R1_001.fastq.gz" 
[10] "RH05_S5_L001_R2_001.fastq.gz"  "RH06_S6_L001_R1_001.fastq.gz"  "RH06_S6_L001_R2_001.fastq.gz" 
[13] "RH07_S7_L001_R1_001.fastq.gz"  "RH07_S7_L001_R2_001.fastq.gz"  "RH08_S8_L001_R1_001.fastq.gz" 
[16] "RH08_S8_L001_R2_001.fastq.gz"  "RH09_S9_L001_R1_001.fastq.gz"  "RH09_S9_L001_R2_001.fastq.gz" 
[19] "RH10_S10_L001_R1_001.fastq.gz" "RH10_S10_L001_R2_001.fastq.gz" "RH11_S11_L001_R1_001.fastq.gz"
[22] "RH11_S11_L001_R2_001.fastq.gz" "RH12_S12_L001_R1_001.fastq.gz" "RH12_S12_L001_R2_001.fastq.gz"
[25] "RH13_S13_L001_R1_001.fastq.gz" "RH13_S13_L001_R2_001.fastq.gz" "RH14_S14_L001_R1_001.fastq.gz"
[28] "RH14_S14_L001_R2_001.fastq.gz" "RH15_S15_L001_R1_001.fastq.gz" "RH15_S15_L001_R2_001.fastq.gz"
[31] "RH16_S16_L001_R1_001.fastq.gz" "RH16_S16_L001_R2_001.fastq.gz" "RH17_S17_L001_R1_001.fastq.gz"
[34] "RH17_S17_L001_R2_001.fastq.gz" "RH18_S18_L001_R1_001.fastq.gz" "RH18_S18_L001_R2_001.fastq.gz"
[37] "RH19_S19_L001_R1_001.fastq.gz" "RH19_S19_L001_R2_001.fastq.gz" "RH20_S20_L001_R1_001.fastq.gz"
[40] "RH20_S20_L001_R2_001.fastq.gz" "RH21_S21_L001_R1_001.fastq.gz" "RH21_S21_L001_R2_001.fastq.gz"
[43] "RH22_S22_L001_R1_001.fastq.gz" "RH22_S22_L001_R2_001.fastq.gz" "RH23_S23_L001_R1_001.fastq.gz"
[46] "RH23_S23_L001_R2_001.fastq.gz" "RH24_S24_L001_R1_001.fastq.gz" "RH24_S24_L001_R2_001.fastq.gz"
[49] "RH25_S25_L001_R1_001.fastq.gz" "RH25_S25_L001_R2_001.fastq.gz" "RH26_S26_L001_R1_001.fastq.gz"
[52] "RH26_S26_L001_R2_001.fastq.gz" "RH27_S27_L001_R1_001.fastq.gz" "RH27_S27_L001_R2_001.fastq.gz"
[55] "RH28_S28_L001_R1_001.fastq.gz" "RH28_S28_L001_R2_001.fastq.gz" "RH29_S29_L001_R1_001.fastq.gz"
[58] "RH29_S29_L001_R2_001.fastq.gz" "RH30_S30_L001_R1_001.fastq.gz" "RH30_S30_L001_R2_001.fastq.gz"
[61] "RH31_S31_L001_R1_001.fastq.gz" "RH31_S31_L001_R2_001.fastq.gz" "RH32_S32_L001_R1_001.fastq.gz"
[64] "RH32_S32_L001_R2_001.fastq.gz" "RH33_S33_L001_R1_001.fastq.gz" "RH33_S33_L001_R2_001.fastq.gz"
[67] "RH34_S34_L001_R1_001.fastq.gz" "RH34_S34_L001_R2_001.fastq.gz" "RH35_S35_L001_R1_001.fastq.gz"
[70] "RH35_S35_L001_R2_001.fastq.gz" "RH36_S36_L001_R1_001.fastq.gz" "RH36_S36_L001_R2_001.fastq.gz"

Quality of reads

In gray-scale is a heat map of the frequency of each quality score at each base position. The median quality score at each position is shown by the green line, and the quartiles of the quality score distribution by the orange lines. The red line shows the scaled proportion of reads that extend to at least that position (this is more useful for other sequencing technologies, as Illumina reads are typically all the same lenghth, hence the flat red line).
Source: DADA2 Pipeline Tutorial (1.12) NOTE: the reason the quality seems to be low at the beginning is that the program is using moving averages so there are less data points in the beginning. No trimming is needed on the left.

Forward reads

   user  system elapsed 
290.668  22.189 313.520 

Reverse reads

   user  system elapsed 
294.714  26.558 322.720 

Filter and trim sequences

The reads were trimmed approximately to the lenght at which the quality score median (the green line) went below 20.
The forward reads were of a very good quiality. Only last 20 bases were trimmed.
The reverse read were of lower quality and were trimmed at the length of 220 bases.

sample.names <- gsub(x = fnFs,
                     pattern = "fastq_jan2020/",
                     replacement = "")
sample.names <- sapply(strsplit(sample.names, "_"),
                       `[`, 
                       1)
sample.names
 [1] "RH01" "RH02" "RH03" "RH04" "RH05" "RH06" "RH07" "RH08" "RH09" "RH10" "RH11" "RH12" "RH13" "RH14" "RH15" "RH16"
[17] "RH17" "RH18" "RH19" "RH20" "RH21" "RH22" "RH23" "RH24" "RH25" "RH26" "RH27" "RH28" "RH29" "RH30" "RH31" "RH32"
[33] "RH33" "RH34" "RH35" "RH36"
filtFs <- gsub(x = fnFs,
               pattern = "fastq_jan2020/",
               replacement = "filtered_jan2020/")
filtRs <- gsub(x = fnRs,
               pattern = "fastq_jan2020/",
               replacement = "filtered_jan2020/")
out <- filterAndTrim(fwd = fnFs, 
                     filt = filtFs,
                     rev = fnRs, 
                     filt.rev = filtRs, 
                     truncLen = c(280, 220),
                     # trimRight = c(20, 80),
                     maxN = 0, 
                     maxEE = c(2, 2), 
                     truncQ = 2, 
                     rm.phix = TRUE, 
                     compress = TRUE,
                     multithread = FALSE)
# NOTE: multi-tread messes up pairs of the files; using single tread instead.
save(out,
     file = "data_jan2020/out.RData")
gc()
           used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells  6994016 373.6   12408701 662.7 11889647 635.0
Vcells 11990477  91.5   66625207 508.4 97695243 745.4

Reads after trimming: examples

Learn the error rates

NOTE: parameter learning is computationally intensive, so by default the learnErrors function uses only a subset of the data (the first 1M reads). If the plotted error model does not look like a good fit, try increasing the nreads parameter to see if the fit improves.

# fnFs <- sort(list.files(path,
#                         pattern="_R1_001.fastq",
#                         full.names = TRUE))
# filtFs <- gsub(x = fnFs,
#                pattern = "fastq_sep2019/",
#                replacement = "filtered_sep2019/")
system.time(errF <- learnErrors(filtFs, 
                                multithread = FALSE))
154626080 total bases in 552236 reads from 2 samples will be used for learning the error rates.
     user    system   elapsed 
26845.522   442.297 27277.245 
save(errF,
     file = "data_jan2020/errF.RData")
# fnRs <- sort(list.files(path,
#                         pattern="_R2_001.fastq",
#                         full.names = TRUE))
# filtRs <- gsub(x = fnRs,
#                pattern = "fastq_sep2019/",
#                replacement = "filtered_sep2019/")

system.time(errR <- learnErrors(filtRs, 
                                multithread = FALSE))
save(errR,
     file = "data_sep2019/errR.RData")

Plot learn the error rates

Dereplicate the dataset

NOTE: for larger datasets (exceeding available RAM) process samples one-by-one. See DADA2 Workflow on Big Data.

# fnFs <- sort(list.files(path, 
#                         pattern="_R1_001.fastq", 
#                         full.names = TRUE))
# filtFs <- gsub(x = fnFs,
#                pattern = "fastq_sep2019/",
#                replacement = "filtered_sep2019/")

system.time(derepFs <- derepFastq(filtFs, 
                                  verbose = TRUE))
save(derepFs,
     file = "data_sep2019/derepFs.RData")

head(derepFs)
gc()
# fnRs <- sort(list.files(path,
#                         pattern="_R2_001.fastq",
#                         full.names = TRUE))
# filtRs <- gsub(x = fnRs,
#                pattern = "fastq_sep2019/",
#                replacement = "filtered_sep2019/")

system.time(derepRs <- derepFastq(filtRs, 
                                  verbose = TRUE))
save(derepRs,
     file = "data_sep2019/derepRs.RData")

head(derepRs)
gc()

Alignment

Notes from IGS Workshop*:
Sample Inference - inferring the sequence variants in each sample.

By default, the dada function processes each sample independently, but pooled processing is available with pool=TRUE and that may give better results for low sampling depths at the cost of increased computation time.

All samples are simultaneously loaded into memory by default. If the datasets approach or exceed available RAM, it is preferable to process samples one-by-one in a streaming fashion: see DADA2 Workflow on Big Data for an example.

# load("data_sep2019/errF.RData")
# load("data_sep2019/derepFs.RData")

system.time(dadaFs <- dada(derep = derepFs, 
                           err = errF,
                           multithread = TRUE))
save(dadaFs,
     file = "data_sep2019/dadaFs.RData")
# load("data_sep2019/errR.RData")
# load("data_sep2019/derepRs.RData")

system.time(dadaRs <- dada(derep = derepRs, 
                           err = errR,
                           multithread = TRUE))
save(dadaRs,
     file = "data_sep2019/dadaRs.RData")

Merge paired reads

Make a sequence table for chimera removal

NOTE: According to the IGS, denovo chimeras are determined based on most abundant sequencins in a given data. Usually 5-7% of sequences are chimeras. It is much higher in this dataset (60.4%). IGS recommends revisiting the removal of primers, as the ambiguous nucleotides in unremoved primers interfere with chimera identification.

Number of reads per sample throughout processing

# load("data_sep2019/out.RData")
# load("data_sep2019/dadaFs.RData")
# fnFs <- sort(list.files(path,
#                         pattern="_R1_001.fastq",
#                         full.names = TRUE))
# sample.names <- gsub(x = fnFs,
#                      pattern = "fastq_sep2019/",
#                      replacement = "")
# sample.names <- sapply(strsplit(sample.names, "_"),
#                        `[`, 
#                        1)
# sample.names

getN <- function(x) {
  sum(getUniques(x))
} 
track <- cbind(out, 
               sapply(dadaFs, 
                      getN),
               sapply(mergers,
                      getN),
               rowSums(seqtab), 
               rowSums(seqtab.nochim))
colnames(track) <- c("Raw", 
                     "Filtered",
                     "Denoised", 
                     "Merged",
                     "Tabled",
                     "Non-Chimeras")
rownames(track) <- sample.names
datatable(format(track,
                 big.mark = ","),
          options = list(pageLength = nrow(track)))

IGS suggests the number of merged sequences can potentially be increased by truncating the reads less (truncLen parameter in the filterAndTrim function), specifically, making sure that the truncated reads span the amplicon. This might not be the case here as the remaining reads are relatively long (280 bases for forward and 220 reads for reverse reads).

Save amplicon sequence variants (ASV) as a FastA file

Write out and save your results thus far:

fc <- file("data_sep2019/all_runs_dada2_ASV.fasta")
fltp <- character()
for( i in 1:ncol(seqtab)) {
  fltp <- append(fltp, 
                 paste0(">Seq_", 
                        i))
  fltp <- append(fltp,
                 colnames(seqtab)[i])
}
writeLines(fltp, 
           fc)
close(fc)
head(fltp)
rm(fltp)
gc()

Assign taxonomy

NOTE: create taxa.RData once, then comment it out and load the R data file to when reruning the code.

# taxa <- assignTaxonomy(seqs = seqtab.nochim,
#                        refFasta = "tax/silva_nr_v132_train_set.fa",
#                        multithread = mt)
# save(taxa,
#      file = "data_sep2019/taxa.RData")

load("data_sep2019/taxa.RData")
print(paste("Number of unique references =",
            format(nrow(taxa),
                   big.mark = ",")))

datatable(taxa[1:5, ],
          rownames = FALSE)

# Keep only the references found in the data
taxa.tmp <- taxa[rownames(taxa) %in% colnames(seqtab.nochim), ]
print(paste("Number of references matched in the data =",
            format(nrow(taxa.tmp),
                   big.mark = ",")))

# # Add species (do it once)
# taxa.plus <- addSpecies(taxtab = taxa.tmp,
#                         refFasta = "tax/silva_species_assignment_v132.fa",
#                         verbose = TRUE)
# save(taxa.plus,
#      file = "data_may2019/taxa.plus.RData")
# 
# load("data_sep2019/taxa.plus.RData")
load("data_sep2019/dt.meta.RData")
dt.otu <- otu_table(seqtab.nochim, 
                    taxa_are_rows = FALSE)
sample_names(dt.otu) <- sample.names
print("Sample names in OTU table")
sample_names(dt.otu)

metadata <- sample_data(dt.meta)
rownames(metadata) <- metadata$SAMPLE_NAME
print("Sample names in metadata")
sample_names(metadata)

metadata@row.names <- sample_names(dt.otu)

ps_sep2019 <- phyloseq(dt.otu, 
                       metadata,
                       tax_table(taxa))
sample_names(ps_sep2019)
save(ps_sep2019,
     file = "data_sep2019/ps_sep2019.RData")

Session Information

sessionInfo()
LS0tCnRpdGxlOiAiUmFzaWthJ3MgMTZTIHNhbXBsZXMsIEphbnVhcnkgMjAyMCBCYXRjaCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQpEYXRlOiBgciBkYXRlKClgICAgICAKU2NpZW50aXN0OiBbUmFuIFlpbl0obWFpbHRvOnJ5MTQ3QHNjYXJsZXRtYWlsLnJ1dGdlcnMuZWR1KSAgICAgIApTZXF1ZW5jaW5nIChXYWtzbWFuKTogW0RpYnllbmR1IEt1bWFyXShtYWlsdG86ZGtAd2Frc21hbi5ydXRnZXJzLmVkdSkgKD8pICAgICAgClN0YXRpc3RpY3M6IFtEYXZpdCBTYXJnc3lhbl0obWFpbHRvOnNhcmdkYXZpZEBnbWFpbC5jb20pICAgICAgClByaW5jaXBhbCBJbnZlc3RpZ2F0b3I6IFtBaC1OZyBLb25nXShtYWlsdG86a29uZ3RAcGhhcm1hY3kucnV0Z2Vycy5lZHUpICAgICAgCgojIFNvdXJjZXMKIyMgU2NyaXB0ClRoaXMgc2NyaXB0IHdhcyBkZXZlbG9wZWQgdXNpbmcgW0RBREEyIFBpcGVsaW5lIFR1dG9yaWFsICgxLjEyKV0oaHR0cHM6Ly9iZW5qam5lYi5naXRodWIuaW8vZGFkYTIvdHV0b3JpYWwuaHRtbCkgd2l0aCB0aXBzIGFuZCB0cmlja3MgZnJvbSB0aGUgW1VuaXZlcnNpdHkgb2YgTWFyeWxhbmQgU2hvb2wgb2YgTWVkaWNpbmUgSW5zdGl0dXRlIGZvciBHZW5vbWUgU2NpZW5jZXMgKElHUyldKGh0dHA6Ly93d3cuaWdzLnVtYXJ5bGFuZC5lZHUvKSBbTWljcm9iaW9tZSBBbmFseXNpcyBXb3Jrc2hvcCAoQXByaWwgOC0xMSwgMjAxOSldKGh0dHA6Ly93d3cuaWdzLnVtYXJ5bGFuZC5lZHUvZWR1Y2F0aW9uL3drc2hwX21ldGFnZW5vbWUucGhwKS4KICAKIyMgRGF0YSAKRmFzdFEgZmlsZXMgd2VyZSBkb3dubG9hZGVkIGZyb20gW3RoaXMgUnV0Z2VycyBCb3ggbG9jYXRpb25dKGh0dHBzOi8vcnV0Z2Vycy5hcHAuYm94LmNvbS9mb2xkZXIvOTAxNDM0NjIyOTEpLiBBIHRvdGFsIG9mIDE0NCBmaWxlcyAoMiBwZXIgc2FtcGxlLCBwYWlyLWVuZGVkKSBhbmQgYSBwYWlyIG9mIHVuZGV0ZXJtaW5lZCByZWFkcyB3ZXJlIGRvd25sb2FkZWQuICAKICAKKioqMTZzIG1ldGFkYXRhIFNlcC0yMDE5Lnhsc3gqKiogbWV0YS1kYXRhIGZpbGUgd2FzIGNyZWF0ZWQgYnkgUmFuLiBJdCB3YXMgc2F2ZWQgYXMgLkNTViBmaWxlIGFuZCB1c2VkIGJ5IHRoaXMgc2NyaXB0LiAgCiAgCiMgTG9hZCBsaWJyYXJpZXMKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0KcmVxdWlyZShrbml0cikKcmVxdWlyZShrYWJsZUV4dHJhKQoKIyAjIEluY3JlYXNlIG1lbW9yeSBzaXplIHRvIDY0IEdiCiMgaW52aXNpYmxlKHV0aWxzOjptZW1vcnkubGltaXQoNjU1MzYpKQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKIyBzdHIoa25pdHI6Om9wdHNfY2h1bmskZ2V0KCkpCiMgIyBOT1RFOiB0aGUgYmVsb3cgZG9lcyBub3Qgd29yayEKIyBrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAojICAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiMgICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gRkFMU0UpCgojIE9uIFdpbmRvd3Mgc2V0IG11bHRpdGhyZWFkPUZBTFNFCiMgT3RoZXJ3aXNlLCBUUlVFIG9yIG51bWJlciBvZiBjb3JlcwojIG10IDwtIDMwCm10IDwtIFRSVUUKCiMgIyBTb3VyY2U6IGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL2luZGV4Lmh0bWwKIyAjIEluc3RhbGxlZCBvbiBKJkogUnN0dWRpbyBzZXJ2ZXIgIG9uIDA1LzIyLzIwMTkKIyBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQojICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKHZlcnNpb24gPSAiMy44IikKIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZGFkYTIiLCB2ZXJzaW9uID0gIjMuOCIpCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoInBoeWxvc2VxIiwgdmVyc2lvbiA9ICIzLjgiKQoKIyBGb2xsb3cgdGhlIHR1dG9yaWFsOgojIGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL3R1dG9yaWFsLmh0bWwKCnJlcXVpcmUoZGF0YS50YWJsZSkKcmVxdWlyZShkYWRhMikKcmVxdWlyZShwaHlsb3NlcSkKcmVxdWlyZShnZ3Bsb3QyKQpsaWJyYXJ5KHN0cmluZ3IpCnJlcXVpcmUoRFQpCgpwYXRoIDwtICJmYXN0cV9qYW4yMDIwIgpgYGAKCiMgTWV0YS1kYXRhCmBgYHtyIG1ldGFfZGF0YX0KIyBkdC5tZXRhIDwtIGZyZWFkKCJkYXRhX2phbjIwMjAvMTZzIG1ldGFkYXRhIFNlcC0yMDE5LmNzdiIpCiMgc2F2ZShkdC5tZXRhLAojICAgICAgZmlsZSA9ICJkYXRhX3NlcDIwMTkvZHQubWV0YS5SRGF0YSIpCiMgZGF0YXRhYmxlKGR0Lm1ldGEsCiMgICAgICAgICAgIGNhcHRpb24gPSAiVGFibGUgMTogTWV0YS1EYXRhIiwKIyAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSwKIyAgICAgICAgICAgY2xhc3MgPSAiY2VsbC1ib3JkZXIgc3RyaXBlIiwKIyAgICAgICAgICAgb3B0aW9ucyA9IGxpc3Qoc2VhcmNoaW5nID0gVFJVRSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDgpKQpgYGAKICAKKipOT1RFKio6IG1vdmVkICJVbmRldGVybWluZWQiIHNhbXBsZXMgZnJvbSB0aGUgRmFzdFEgZm9sZGVyIHRvIHRoZSBkb2NzIGZvbGRlci4gIAogIAojIFF1ZXN0aW9ucwoxLiBEaWQgbWljcm9iaW9tZSBjaGFuZ2Ugb3ZlciB0aW1lPyAgIAoyLiBXYXMgbWljcm9iaW9tZSBhZmZlY3RlZCBieSBkaWV0PyAgCjMuIFdhcyBtaWNyb2Jpb21lIGFmZmVjdGVkIGJ5IEtPIGNvbXBhcmVkIHRvIFdUPyAgCiAgCiMgRmFzdFEgZmlsZXMKYGBge3IgZmFzdHF9CiMgR2V0IEZhc3RRIGZpbGUgbmFtZXMtLS0tCmxpc3QuZmlsZXMocGF0aCA9IHBhdGgsCiAgICAgICAgICAgcGF0dGVybiA9ICIuZ3oiKQpgYGAKCiMgUXVhbGl0eSBvZiByZWFkcwpJbiAqKmdyYXktc2NhbGUqKiBpcyBhIGhlYXQgbWFwIG9mIHRoZSBmcmVxdWVuY3kgb2YgZWFjaCBxdWFsaXR5IHNjb3JlIGF0IGVhY2ggYmFzZSBwb3NpdGlvbi4gVGhlIG1lZGlhbiBxdWFsaXR5IHNjb3JlIGF0IGVhY2ggcG9zaXRpb24gaXMgc2hvd24gYnkgdGhlICoqZ3JlZW4qKiBsaW5lLCBhbmQgdGhlIHF1YXJ0aWxlcyBvZiB0aGUgcXVhbGl0eSBzY29yZSBkaXN0cmlidXRpb24gYnkgdGhlICoqb3JhbmdlKiogbGluZXMuIFRoZSAqKnJlZCoqIGxpbmUgc2hvd3MgdGhlIHNjYWxlZCBwcm9wb3J0aW9uIG9mIHJlYWRzIHRoYXQgZXh0ZW5kIHRvIGF0IGxlYXN0IHRoYXQgcG9zaXRpb24gKHRoaXMgaXMgbW9yZSB1c2VmdWwgZm9yIG90aGVyIHNlcXVlbmNpbmcgdGVjaG5vbG9naWVzLCBhcyBJbGx1bWluYSByZWFkcyBhcmUgdHlwaWNhbGx5IGFsbCB0aGUgc2FtZSBsZW5naHRoLCBoZW5jZSB0aGUgZmxhdCByZWQgbGluZSkuICAgICAgClNvdXJjZTogW0RBREEyIFBpcGVsaW5lIFR1dG9yaWFsICgxLjEyKV0oaHR0cHM6Ly9iZW5qam5lYi5naXRodWIuaW8vZGFkYTIvdHV0b3JpYWwuaHRtbCkgCioqTk9URSoqOiB0aGUgcmVhc29uIHRoZSBxdWFsaXR5IHNlZW1zIHRvIGJlIGxvdyBhdCB0aGUgYmVnaW5uaW5nIGlzIHRoYXQgdGhlIHByb2dyYW0gaXMgdXNpbmcgbW92aW5nIGF2ZXJhZ2VzIHNvIHRoZXJlIGFyZSBsZXNzIGRhdGEgcG9pbnRzIGluIHRoZSBiZWdpbm5pbmcuIE5vIHRyaW1taW5nIGlzIG5lZWRlZCBvbiB0aGUgbGVmdC4KCiMjIEZvcndhcmQgcmVhZHMKYGBge3IgcGxvdF9xdWFsaXR5X2Z3ZCwgd2FybmluZ3MgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA1fQpmbkZzIDwtIHNvcnQobGlzdC5maWxlcyhwYXRoLCAKICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybj0iX1IxXzAwMS5mYXN0cSIsIAogICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCnN5c3RlbS50aW1lKHsKICBmb3IgKGkgaW4gMTpsZW5ndGgoZm5GcykpIHsKICAgIHByaW50KHBsb3RRdWFsaXR5UHJvZmlsZShmbkZzW2ldKSkKICB9Cn0pCmBgYAoKIyMgUmV2ZXJzZSByZWFkcwpgYGB7ciBwbG90X3F1YWxpdHlfcmV2LCB3YXJuaW5ncyA9IEZBTFNFLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDV9CmZuUnMgPC0gc29ydChsaXN0LmZpbGVzKHBhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49Il9SMl8wMDEuZmFzdHEiLAogICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCnN5c3RlbS50aW1lKHsKICBmb3IgKGkgaW4gMTpsZW5ndGgoZm5ScykpIHsKICAgIHByaW50KHBsb3RRdWFsaXR5UHJvZmlsZShmblJzW2ldKSkKICB9Cn0pCmBgYAoKIyBGaWx0ZXIgYW5kIHRyaW0gc2VxdWVuY2VzClRoZSByZWFkcyB3ZXJlIHRyaW1tZWQgYXBwcm94aW1hdGVseSB0byB0aGUgbGVuZ2h0IGF0IHdoaWNoIHRoZSBxdWFsaXR5IHNjb3JlIG1lZGlhbiAodGhlICoqZ3JlZW4qKiBsaW5lKSB3ZW50IGJlbG93IDIwLiAgICAKVGhlIGZvcndhcmQgcmVhZHMgd2VyZSBvZiBhIHZlcnkgZ29vZCBxdWlhbGl0eS4gT25seSBsYXN0IDIwIGJhc2VzIHdlcmUgdHJpbW1lZC4gICAgClRoZSByZXZlcnNlIHJlYWQgd2VyZSBvZiBsb3dlciBxdWFsaXR5IGFuZCB3ZXJlIHRyaW1tZWQgYXQgdGhlIGxlbmd0aCBvZiAyMjAgYmFzZXMuICAKICAKYGBge3Igc29ydF9uX3RyaW1fcHJlcH0Kc2FtcGxlLm5hbWVzIDwtIGdzdWIoeCA9IGZuRnMsCiAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiZmFzdHFfamFuMjAyMC8iLAogICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIiKQpzYW1wbGUubmFtZXMgPC0gc2FwcGx5KHN0cnNwbGl0KHNhbXBsZS5uYW1lcywgIl8iKSwKICAgICAgICAgICAgICAgICAgICAgICBgW2AsIAogICAgICAgICAgICAgICAgICAgICAgIDEpCnNhbXBsZS5uYW1lcwoKZmlsdEZzIDwtIGdzdWIoeCA9IGZuRnMsCiAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiZmFzdHFfamFuMjAyMC8iLAogICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJmaWx0ZXJlZF9qYW4yMDIwLyIpCgpmaWx0UnMgPC0gZ3N1Yih4ID0gZm5ScywKICAgICAgICAgICAgICAgcGF0dGVybiA9ICJmYXN0cV9qYW4yMDIwLyIsCiAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gImZpbHRlcmVkX2phbjIwMjAvIikKYGBgCgpgYGB7ciBzb3J0X25fdHJpbX0Kb3V0IDwtIGZpbHRlckFuZFRyaW0oZndkID0gZm5GcywgCiAgICAgICAgICAgICAgICAgICAgIGZpbHQgPSBmaWx0RnMsCiAgICAgICAgICAgICAgICAgICAgIHJldiA9IGZuUnMsIAogICAgICAgICAgICAgICAgICAgICBmaWx0LnJldiA9IGZpbHRScywgCiAgICAgICAgICAgICAgICAgICAgIHRydW5jTGVuID0gYygyODAsIDIyMCksCiAgICAgICAgICAgICAgICAgICAgICMgdHJpbVJpZ2h0ID0gYygyMCwgODApLAogICAgICAgICAgICAgICAgICAgICBtYXhOID0gMCwgCiAgICAgICAgICAgICAgICAgICAgIG1heEVFID0gYygyLCAyKSwgCiAgICAgICAgICAgICAgICAgICAgIHRydW5jUSA9IDIsIAogICAgICAgICAgICAgICAgICAgICBybS5waGl4ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgIGNvbXByZXNzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgbXVsdGl0aHJlYWQgPSBGQUxTRSkKIyBOT1RFOiBtdWx0aS10cmVhZCBtZXNzZXMgdXAgcGFpcnMgb2YgdGhlIGZpbGVzOyB1c2luZyBzaW5nbGUgdHJlYWQgaW5zdGVhZC4KCnNhdmUob3V0LAogICAgIGZpbGUgPSAiZGF0YV9qYW4yMDIwL291dC5SRGF0YSIpCmdjKCkKYGBgCgojIFJlYWRzIGFmdGVyIHRyaW1taW5nOiBleGFtcGxlcwpgYGB7ciB0cmltX3Jldix3YXJuaW5ncz1GQUxTRSxlY2hvPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CnBsb3RRdWFsaXR5UHJvZmlsZShmaWx0RnNbMV0pCnBsb3RRdWFsaXR5UHJvZmlsZShmaWx0UnNbMV0pCmBgYAoKIyBMZWFybiB0aGUgZXJyb3IgcmF0ZXMKKipOT1RFKio6IHBhcmFtZXRlciBsZWFybmluZyBpcyBjb21wdXRhdGlvbmFsbHkgaW50ZW5zaXZlLCBzbyBieSBkZWZhdWx0IHRoZSBsZWFybkVycm9ycyBmdW5jdGlvbiB1c2VzIG9ubHkgYSBzdWJzZXQgb2YgdGhlIGRhdGEgKHRoZSBmaXJzdCAxTSByZWFkcykuIElmIHRoZSBwbG90dGVkIGVycm9yIG1vZGVsIGRvZXMgbm90IGxvb2sgbGlrZSBhIGdvb2QgZml0LCB0cnkgaW5jcmVhc2luZyB0aGUgbnJlYWRzIHBhcmFtZXRlciB0byBzZWUgaWYgdGhlIGZpdCBpbXByb3Zlcy4KYGBge3IgbGVhcm5fZXJyb3JfZnN9CiMgZm5GcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwKIyAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPSJfUjFfMDAxLmZhc3RxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiMgZmlsdEZzIDwtIGdzdWIoeCA9IGZuRnMsCiMgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJmYXN0cV9zZXAyMDE5LyIsCiMgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiZmlsdGVyZWRfc2VwMjAxOS8iKQoKc3lzdGVtLnRpbWUoZXJyRiA8LSBsZWFybkVycm9ycyhmaWx0RnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpdGhyZWFkID0gRkFMU0UpKQpzYXZlKGVyckYsCiAgICAgZmlsZSA9ICJkYXRhX2phbjIwMjAvZXJyRi5SRGF0YSIpCmBgYAoKYGBge3IgbGVhcm5fZXJyb3JfcnN9CiMgZm5ScyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwKIyAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPSJfUjJfMDAxLmZhc3RxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiMgZmlsdFJzIDwtIGdzdWIoeCA9IGZuUnMsCiMgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJmYXN0cV9zZXAyMDE5LyIsCiMgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiZmlsdGVyZWRfc2VwMjAxOS8iKQoKc3lzdGVtLnRpbWUoZXJyUiA8LSBsZWFybkVycm9ycyhmaWx0UnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpdGhyZWFkID0gRkFMU0UpKQpzYXZlKGVyclIsCiAgICAgZmlsZSA9ICJkYXRhX3NlcDIwMTkvZXJyUi5SRGF0YSIpCmBgYAoKIyBQbG90IGxlYXJuIHRoZSBlcnJvciByYXRlcwpgYGB7ciBwbG90X2Vycm9yLHdhcm5pbmdzPUZBTFNFLGVjaG89RkFMU0UsbWVzc2FnZT1GQUxTRX0KcGxvdEVycm9ycyhlcnJGLCAKICAgICAgICAgICBub21pbmFsUSA9IFRSVUUpCnBsb3RFcnJvcnMoZXJyUiwgCiAgICAgICAgICAgbm9taW5hbFEgPSBUUlVFKQpgYGAKCiMgRGVyZXBsaWNhdGUgdGhlIGRhdGFzZXQgCioqTk9URSoqOiBmb3IgbGFyZ2VyIGRhdGFzZXRzIChleGNlZWRpbmcgYXZhaWxhYmxlIFJBTSkgcHJvY2VzcyBzYW1wbGVzIG9uZS1ieS1vbmUuIFNlZSBbREFEQTIgV29ya2Zsb3cgb24gQmlnIERhdGFdKGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL2JpZ2RhdGEuaHRtbCkuCmBgYHtyIGRlcmVwbGljYXRlX2ZzfQojIGZuRnMgPC0gc29ydChsaXN0LmZpbGVzKHBhdGgsIAojICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49Il9SMV8wMDEuZmFzdHEiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiMgZmlsdEZzIDwtIGdzdWIoeCA9IGZuRnMsCiMgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJmYXN0cV9zZXAyMDE5LyIsCiMgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiZmlsdGVyZWRfc2VwMjAxOS8iKQoKc3lzdGVtLnRpbWUoZGVyZXBGcyA8LSBkZXJlcEZhc3RxKGZpbHRGcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkpCnNhdmUoZGVyZXBGcywKICAgICBmaWxlID0gImRhdGFfc2VwMjAxOS9kZXJlcEZzLlJEYXRhIikKCmhlYWQoZGVyZXBGcykKZ2MoKQpgYGAKCmBgYHtyIGRlcmVwbGljYXRlX3JzfQojIGZuUnMgPC0gc29ydChsaXN0LmZpbGVzKHBhdGgsCiMgICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybj0iX1IyXzAwMS5mYXN0cSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpKQojIGZpbHRScyA8LSBnc3ViKHggPSBmblJzLAojICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiZmFzdHFfc2VwMjAxOS8iLAojICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gImZpbHRlcmVkX3NlcDIwMTkvIikKCnN5c3RlbS50aW1lKGRlcmVwUnMgPC0gZGVyZXBGYXN0cShmaWx0UnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpKQpzYXZlKGRlcmVwUnMsCiAgICAgZmlsZSA9ICJkYXRhX3NlcDIwMTkvZGVyZXBScy5SRGF0YSIpCgpoZWFkKGRlcmVwUnMpCmdjKCkKYGBgCgojIEFsaWdubWVudAoqKk5vdGVzIGZyb20gSUdTIFdvcmtzaG9wKioqOiAgICAKU2FtcGxlIEluZmVyZW5jZSAtIGluZmVycmluZyB0aGUgc2VxdWVuY2UgdmFyaWFudHMgaW4gZWFjaCBzYW1wbGUuICAgICAKICAgICAgCkJ5IGRlZmF1bHQsIHRoZSAqKipkYWRhKioqIGZ1bmN0aW9uIHByb2Nlc3NlcyBlYWNoIHNhbXBsZSBpbmRlcGVuZGVudGx5LCBidXQgcG9vbGVkIHByb2Nlc3NpbmcgaXMgYXZhaWxhYmxlIHdpdGggKioqcG9vbD1UUlVFKioqIGFuZCB0aGF0IG1heSBnaXZlIGJldHRlciByZXN1bHRzIGZvciBsb3cgc2FtcGxpbmcgZGVwdGhzIGF0IHRoZSBjb3N0IG9mIGluY3JlYXNlZCBjb21wdXRhdGlvbiB0aW1lLiAgICAgCiAgICAgCkFsbCBzYW1wbGVzIGFyZSBzaW11bHRhbmVvdXNseSBsb2FkZWQgaW50byBtZW1vcnkgYnkgZGVmYXVsdC4gSWYgdGhlIGRhdGFzZXRzIGFwcHJvYWNoIG9yIGV4Y2VlZCBhdmFpbGFibGUgUkFNLCBpdCBpcyBwcmVmZXJhYmxlIHRvIHByb2Nlc3Mgc2FtcGxlcyBvbmUtYnktb25lIGluIGEgc3RyZWFtaW5nIGZhc2hpb246IHNlZSBbREFEQTIgV29ya2Zsb3cgb24gQmlnIERhdGFdKGh0dHBzOi8vYmVuampuZWIuZ2l0aHViLmlvL2RhZGEyL2JpZ2RhdGEuaHRtbCkgZm9yIGFuIGV4YW1wbGUuICAgIApgYGB7ciBkYWRhX2ZzfQojIGxvYWQoImRhdGFfc2VwMjAxOS9lcnJGLlJEYXRhIikKIyBsb2FkKCJkYXRhX3NlcDIwMTkvZGVyZXBGcy5SRGF0YSIpCgpzeXN0ZW0udGltZShkYWRhRnMgPC0gZGFkYShkZXJlcCA9IGRlcmVwRnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBlcnIgPSBlcnJGLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aXRocmVhZCA9IFRSVUUpKQpzYXZlKGRhZGFGcywKICAgICBmaWxlID0gImRhdGFfc2VwMjAxOS9kYWRhRnMuUkRhdGEiKQpgYGAKCmBgYHtyIGRhZGFfcnN9CiMgbG9hZCgiZGF0YV9zZXAyMDE5L2VyclIuUkRhdGEiKQojIGxvYWQoImRhdGFfc2VwMjAxOS9kZXJlcFJzLlJEYXRhIikKCnN5c3RlbS50aW1lKGRhZGFScyA8LSBkYWRhKGRlcmVwID0gZGVyZXBScywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGVyciA9IGVyclIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpdGhyZWFkID0gVFJVRSkpCnNhdmUoZGFkYVJzLAogICAgIGZpbGUgPSAiZGF0YV9zZXAyMDE5L2RhZGFScy5SRGF0YSIpCmBgYAoKIyBNZXJnZSBwYWlyZWQgcmVhZHMKYGBge3IgbWVyZ2Usd2FybmluZ3M9RkFMU0UsZWNobz1GQUxTRSxtZXNzYWdlPUZBTFNFLGV2YWw9VFJVRX0KIyBsb2FkKCJkYXRhX3NlcDIwMTkvZGFkYUZzLlJEYXRhIikKIyBsb2FkKCJkYXRhX3NlcDIwMTkvZGVyZXBGcy5SRGF0YSIpCiMgbG9hZCgiZGF0YV9zZXAyMDE5L2RhZGFScy5SRGF0YSIpCiMgbG9hZCgiZGF0YV9zZXAyMDE5L2RlcmVwUnMuUkRhdGEiKQoKc3lzdGVtLnRpbWUobWVyZ2VycyA8LSBtZXJnZVBhaXJzKGRhZGFGID0gZGFkYUZzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVyZXBGID0gZGVyZXBGcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhZGFSID0gZGFkYVJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlcmVwUiA9IGRlcmVwUnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkpCnNhdmUobWVyZ2VycywKICAgICBmaWxlID0gImRhdGFfc2VwMjAxOS9tZXJnZXJzLlJEYXRhIikKYGBgCgojIE1ha2UgYSBzZXF1ZW5jZSB0YWJsZSBmb3IgY2hpbWVyYSByZW1vdmFsCmBgYHtyIGNoaW1lcmEsd2FybmluZ3M9RkFMU0UsZWNobz1GQUxTRSxtZXNzYWdlPUZBTFNFLGV2YWw9VFJVRX0KIyBsb2FkKCJkYXRhX3NlcDIwMTkvbWVyZ2Vycy5SRGF0YSIpCgpzeXN0ZW0udGltZShzZXF0YWIgPC0gbWFrZVNlcXVlbmNlVGFibGUobWVyZ2VycykpCgpkaW0oc2VxdGFiKQpzYXZlKHNlcXRhYiwKICAgICBmaWxlID0gImRhdGFfc2VwMjAxOS9zZXF0YWIuUkRhdGEiKQpnYygpCgojIFJlbW92ZSBjaGltZXJhcwpzeXN0ZW0udGltZShzZXF0YWIubm9jaGltIDwtIHJlbW92ZUJpbWVyYURlbm92byh1bnFzID0gc2VxdGFiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiY29uc2Vuc3VzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXVsdGl0aHJlYWQgPSBtdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpKQoKZGltKHNlcXRhYi5ub2NoaW0pIApzYXZlKHNlcXRhYi5ub2NoaW0sCiAgICAgZmlsZSA9ICJkYXRhX21heTIwMTkvc2VxdGFiLm5vY2hpbS5SRGF0YSIpCndyaXRlLmNzdihzZXF0YWIubm9jaGltLCAKICAgICAgICAgIGZpbGUgPSAiZGF0YV9tYXkyMDE5L3NlcXRhYi5ub2NoaW0uY3N2IiwgCiAgICAgICAgICBxdW90ZSA9IEZBTFNFKQoKIyAxIC0gcHJvcG9ydGlvbiBvZiBjaGltZXJhcwpwcmludChwYXN0ZSgiQ2hpbWVyYXMgPSAiLAogICAgICAgICAgICByb3VuZCgxMDAqKDEgLSAoc3VtKHNlcXRhYi5ub2NoaW0pL3N1bShzZXF0YWIpKSksIDEpLAogICAgICAgICAgICAiJSIsCiAgICAgICAgICAgIHNlcCA9ICIiKSkKYGBgCgoqKk5PVEUqKjogQWNjb3JkaW5nIHRvIHRoZSBJR1MsIGRlbm92byBjaGltZXJhcyBhcmUgZGV0ZXJtaW5lZCBiYXNlZCBvbiBtb3N0IGFidW5kYW50IHNlcXVlbmNpbnMgaW4gYSBnaXZlbiBkYXRhLiBVc3VhbGx5IDUtNyUgb2Ygc2VxdWVuY2VzIGFyZSBjaGltZXJhcy4gSXQgaXMgbXVjaCBoaWdoZXIgaW4gdGhpcyBkYXRhc2V0ICg2MC40JSkuIElHUyByZWNvbW1lbmRzIHJldmlzaXRpbmcgdGhlIHJlbW92YWwgb2YgcHJpbWVycywgYXMgdGhlIGFtYmlndW91cyBudWNsZW90aWRlcyBpbiB1bnJlbW92ZWQgcHJpbWVycyBpbnRlcmZlcmUgd2l0aCBjaGltZXJhIGlkZW50aWZpY2F0aW9uLiAKCiMjIE51bWJlciBvZiByZWFkcyBwZXIgc2FtcGxlIHRocm91Z2hvdXQgcHJvY2Vzc2luZwpgYGB7ciBjaGVja19sZW5ndGh9CiMgbG9hZCgiZGF0YV9zZXAyMDE5L291dC5SRGF0YSIpCiMgbG9hZCgiZGF0YV9zZXAyMDE5L2RhZGFGcy5SRGF0YSIpCiMgZm5GcyA8LSBzb3J0KGxpc3QuZmlsZXMocGF0aCwKIyAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuPSJfUjFfMDAxLmZhc3RxIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSkpCiMgc2FtcGxlLm5hbWVzIDwtIGdzdWIoeCA9IGZuRnMsCiMgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybiA9ICJmYXN0cV9zZXAyMDE5LyIsCiMgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiIikKIyBzYW1wbGUubmFtZXMgPC0gc2FwcGx5KHN0cnNwbGl0KHNhbXBsZS5uYW1lcywgIl8iKSwKIyAgICAgICAgICAgICAgICAgICAgICAgIGBbYCwgCiMgICAgICAgICAgICAgICAgICAgICAgICAxKQojIHNhbXBsZS5uYW1lcwoKZ2V0TiA8LSBmdW5jdGlvbih4KSB7CiAgc3VtKGdldFVuaXF1ZXMoeCkpCn0gCnRyYWNrIDwtIGNiaW5kKG91dCwgCiAgICAgICAgICAgICAgIHNhcHBseShkYWRhRnMsIAogICAgICAgICAgICAgICAgICAgICAgZ2V0TiksCiAgICAgICAgICAgICAgIHNhcHBseShtZXJnZXJzLAogICAgICAgICAgICAgICAgICAgICAgZ2V0TiksCiAgICAgICAgICAgICAgIHJvd1N1bXMoc2VxdGFiKSwgCiAgICAgICAgICAgICAgIHJvd1N1bXMoc2VxdGFiLm5vY2hpbSkpCmNvbG5hbWVzKHRyYWNrKSA8LSBjKCJSYXciLCAKICAgICAgICAgICAgICAgICAgICAgIkZpbHRlcmVkIiwKICAgICAgICAgICAgICAgICAgICAgIkRlbm9pc2VkIiwgCiAgICAgICAgICAgICAgICAgICAgICJNZXJnZWQiLAogICAgICAgICAgICAgICAgICAgICAiVGFibGVkIiwKICAgICAgICAgICAgICAgICAgICAgIk5vbi1DaGltZXJhcyIpCnJvd25hbWVzKHRyYWNrKSA8LSBzYW1wbGUubmFtZXMKZGF0YXRhYmxlKGZvcm1hdCh0cmFjaywKICAgICAgICAgICAgICAgICBiaWcubWFyayA9ICIsIiksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gbnJvdyh0cmFjaykpKQpgYGAKCklHUyBzdWdnZXN0cyB0aGUgbnVtYmVyIG9mICoqbWVyZ2VkKiogc2VxdWVuY2VzIGNhbiBwb3RlbnRpYWxseSBiZSBpbmNyZWFzZWQgYnkgdHJ1bmNhdGluZyB0aGUgcmVhZHMgbGVzcyAoKioqdHJ1bmNMZW4qKiogcGFyYW1ldGVyIGluIHRoZSAqKipmaWx0ZXJBbmRUcmltKioqIGZ1bmN0aW9uKSwgc3BlY2lmaWNhbGx5LCBtYWtpbmcgc3VyZSB0aGF0IHRoZSB0cnVuY2F0ZWQgcmVhZHMgc3BhbiB0aGUgYW1wbGljb24uIFRoaXMgbWlnaHQgbm90IGJlIHRoZSBjYXNlIGhlcmUgYXMgdGhlIHJlbWFpbmluZyByZWFkcyBhcmUgcmVsYXRpdmVseSBsb25nICgyODAgYmFzZXMgZm9yIGZvcndhcmQgYW5kIDIyMCByZWFkcyBmb3IgcmV2ZXJzZSByZWFkcykuCgojIFNhdmUgYW1wbGljb24gc2VxdWVuY2UgdmFyaWFudHMgKEFTVikgYXMgYSBGYXN0QSBmaWxlCldyaXRlIG91dCBhbmQgc2F2ZSB5b3VyIHJlc3VsdHMgdGh1cyBmYXI6IApgYGB7ciBzYXZlX2Zhc3RhX2Fzdn0KZmMgPC0gZmlsZSgiZGF0YV9zZXAyMDE5L2FsbF9ydW5zX2RhZGEyX0FTVi5mYXN0YSIpCmZsdHAgPC0gY2hhcmFjdGVyKCkKZm9yKCBpIGluIDE6bmNvbChzZXF0YWIpKSB7CiAgZmx0cCA8LSBhcHBlbmQoZmx0cCwgCiAgICAgICAgICAgICAgICAgcGFzdGUwKCI+U2VxXyIsIAogICAgICAgICAgICAgICAgICAgICAgICBpKSkKICBmbHRwIDwtIGFwcGVuZChmbHRwLAogICAgICAgICAgICAgICAgIGNvbG5hbWVzKHNlcXRhYilbaV0pCn0Kd3JpdGVMaW5lcyhmbHRwLCAKICAgICAgICAgICBmYykKY2xvc2UoZmMpCmhlYWQoZmx0cCkKcm0oZmx0cCkKZ2MoKQpgYGAKCiMgQXNzaWduIHRheG9ub215CioqTk9URSoqOiBjcmVhdGUgKioqdGF4YS5SRGF0YSoqKiBvbmNlLCB0aGVuIGNvbW1lbnQgaXQgb3V0IGFuZCBsb2FkIHRoZSBSIGRhdGEgZmlsZSB0byB3aGVuIHJlcnVuaW5nIHRoZSBjb2RlLiAKYGBge3IgdGF4X2Fzc2lnbn0KIyB0YXhhIDwtIGFzc2lnblRheG9ub215KHNlcXMgPSBzZXF0YWIubm9jaGltLAojICAgICAgICAgICAgICAgICAgICAgICAgcmVmRmFzdGEgPSAidGF4L3NpbHZhX25yX3YxMzJfdHJhaW5fc2V0LmZhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgIG11bHRpdGhyZWFkID0gbXQpCiMgc2F2ZSh0YXhhLAojICAgICAgZmlsZSA9ICJkYXRhX3NlcDIwMTkvdGF4YS5SRGF0YSIpCgpsb2FkKCJkYXRhX3NlcDIwMTkvdGF4YS5SRGF0YSIpCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgdW5pcXVlIHJlZmVyZW5jZXMgPSIsCiAgICAgICAgICAgIGZvcm1hdChucm93KHRheGEpLAogICAgICAgICAgICAgICAgICAgYmlnLm1hcmsgPSAiLCIpKSkKCmRhdGF0YWJsZSh0YXhhWzE6NSwgXSwKICAgICAgICAgIHJvd25hbWVzID0gRkFMU0UpCgojIEtlZXAgb25seSB0aGUgcmVmZXJlbmNlcyBmb3VuZCBpbiB0aGUgZGF0YQp0YXhhLnRtcCA8LSB0YXhhW3Jvd25hbWVzKHRheGEpICVpbiUgY29sbmFtZXMoc2VxdGFiLm5vY2hpbSksIF0KcHJpbnQocGFzdGUoIk51bWJlciBvZiByZWZlcmVuY2VzIG1hdGNoZWQgaW4gdGhlIGRhdGEgPSIsCiAgICAgICAgICAgIGZvcm1hdChucm93KHRheGEudG1wKSwKICAgICAgICAgICAgICAgICAgIGJpZy5tYXJrID0gIiwiKSkpCgojICMgQWRkIHNwZWNpZXMgKGRvIGl0IG9uY2UpCiMgdGF4YS5wbHVzIDwtIGFkZFNwZWNpZXModGF4dGFiID0gdGF4YS50bXAsCiMgICAgICAgICAgICAgICAgICAgICAgICAgcmVmRmFzdGEgPSAidGF4L3NpbHZhX3NwZWNpZXNfYXNzaWdubWVudF92MTMyLmZhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkKIyBzYXZlKHRheGEucGx1cywKIyAgICAgIGZpbGUgPSAiZGF0YV9tYXkyMDE5L3RheGEucGx1cy5SRGF0YSIpCiMgCiMgbG9hZCgiZGF0YV9zZXAyMDE5L3RheGEucGx1cy5SRGF0YSIpCmBgYAoKYGBge3IgcGh5bG9zZXF9CmxvYWQoImRhdGFfc2VwMjAxOS9kdC5tZXRhLlJEYXRhIikKZHQub3R1IDwtIG90dV90YWJsZShzZXF0YWIubm9jaGltLCAKICAgICAgICAgICAgICAgICAgICB0YXhhX2FyZV9yb3dzID0gRkFMU0UpCnNhbXBsZV9uYW1lcyhkdC5vdHUpIDwtIHNhbXBsZS5uYW1lcwpwcmludCgiU2FtcGxlIG5hbWVzIGluIE9UVSB0YWJsZSIpCnNhbXBsZV9uYW1lcyhkdC5vdHUpCgptZXRhZGF0YSA8LSBzYW1wbGVfZGF0YShkdC5tZXRhKQpyb3duYW1lcyhtZXRhZGF0YSkgPC0gbWV0YWRhdGEkU0FNUExFX05BTUUKcHJpbnQoIlNhbXBsZSBuYW1lcyBpbiBtZXRhZGF0YSIpCnNhbXBsZV9uYW1lcyhtZXRhZGF0YSkKCm1ldGFkYXRhQHJvdy5uYW1lcyA8LSBzYW1wbGVfbmFtZXMoZHQub3R1KQoKcHNfc2VwMjAxOSA8LSBwaHlsb3NlcShkdC5vdHUsIAogICAgICAgICAgICAgICAgICAgICAgIG1ldGFkYXRhLAogICAgICAgICAgICAgICAgICAgICAgIHRheF90YWJsZSh0YXhhKSkKc2FtcGxlX25hbWVzKHBzX3NlcDIwMTkpCnNhdmUocHNfc2VwMjAxOSwKICAgICBmaWxlID0gImRhdGFfc2VwMjAxOS9wc19zZXAyMDE5LlJEYXRhIikKYGBgCgojIFNlc3Npb24gSW5mb3JtYXRpb24KYGBge3IgaW5mbyxldmFsPVRSVUV9CnNlc3Npb25JbmZvKCkKYGBg